package worms;

import gameengine.*;
import java.awt.*;
import java.util.Vector;

/**
 * Classe que representa um jogador
 * @author Victor
 */
public class Player extends SpriteAnimada
{

	//Estados de direção da arma
	public static final String DIRECAO_NORMAL = "normal";
	public static final String DIRECAO_BAIXO = "baixo";
	public static final String DIRECAO_MEDIO = "medio";
	public static final String DIRECAO_ALTO = "alto";
	public static final String DIRECAO_GIGANTE = "gigante";
	//Angulações dos tiros
	public static final int ANGULO_NORMAL = 0;
	public static final int ANGULO_BAIXO = 15;
	public static final int ANGULO_MEDIO = -15;
	public static final int ANGULO_ALTO = -30;
	public static final int ANGULO_GIGANTE = -60;
	//Offsets da arma em Y
	public static final int OFFSET_Y_NORMAL = 57;
	public static final int OFFSET_Y_BAIXO = 65;
	public static final int OFFSET_Y_MEDIO = 42;
	public static final int OFFSET_Y_ALTO = 25;
	public static final int OFFSET_Y_GIGANTE = 3;
	public static final String[] DIRECOES = new String[]
	{
		DIRECAO_BAIXO, DIRECAO_NORMAL, DIRECAO_MEDIO, DIRECAO_ALTO, DIRECAO_GIGANTE
	};
	public static final int[] ANGULOS = new int[]
	{
		ANGULO_BAIXO, ANGULO_NORMAL, ANGULO_MEDIO, ANGULO_ALTO, ANGULO_GIGANTE
	};
	public static final int[] OFFSETS_Y = new int[]
	{
		OFFSET_Y_NORMAL, OFFSET_Y_BAIXO, OFFSET_Y_MEDIO, OFFSET_Y_ALTO, OFFSET_Y_GIGANTE
	};
	//Estados do jogador
	public static final int ESTADO_ANDANDO_TRAS = -1;
	public static final int ESTADO_ANDANDO_FRENTE = 1;
	public static final int ESTADO_PARADO = 0;
	//Lados da tela
	public static final int LADO_ESQUERDO = -1;
	public static final int LADO_DIREITO = 1;
	public static final int VIDA_MAXIMA = 5;
	private static final String PREFIXO_INIMIGO = "p2";
	private static final int VELOCIDADE = 5;
    private static final float GRAVIDADE = 0.5f; 
	private Vector<PlayerStateListener> listeners = new Vector<PlayerStateListener>();
	private int _id;
	private int _direcaoAtual;
	private int _estadoAtual = ESTADO_PARADO;
	private int _ladoAtual;
	private int _vidaAtual = VIDA_MAXIMA;
	private boolean _isMorto = false;
	private String _prefix;
	private GameContainer container;
	private float velocidadeY;
	private boolean isPulando = false;

	public void SetVelocidadeY(float velocidade)
	{
		velocidadeY = velocidade;
	}

	public float GetVelocidadeY()
	{
		return velocidadeY;
	}

	/**
	 * Carrega as animações do jogador
	 * @owner Victor
	 */
	private static final Animacao[] LoadAnimacoes(String prefix)
	{
		Animacao animacaoAlto = new Animacao(prefix + "_alto_walk", new Textura("./src/worms/images/" + prefix + "_alto_walk.png"), new Dimension(103, 104), 20);
		Animacao animacaoBaixo = new Animacao(prefix + "_baixo_walk", new Textura("./src/worms/images/" + prefix + "_baixo_walk.png"), new Dimension(103, 104), 20);
		Animacao animacaoGigante = new Animacao(prefix + "_gigante_walk", new Textura("./src/worms/images/" + prefix + "_gigante_walk.png"), new Dimension(103, 104), 20);
		Animacao animacaoMedio = new Animacao(prefix + "_medio_walk", new Textura("./src/worms/images/" + prefix + "_medio_walk.png"), new Dimension(103, 104), 20);
		Animacao animacaoNormal = new Animacao(prefix + "_normal_walk", new Textura("./src/worms/images/" + prefix + "_normal_walk.png"), new Dimension(103, 104), 20);
		Animacao animacaoIddleAlto = new Animacao(prefix + "_iddle_alto", new Textura("./src/worms/images/" + prefix + "_iddle_alto.png"), new Dimension(103, 104), 20);
		Animacao animacaoIddleBaixo = new Animacao(prefix + "_iddle_baixo", new Textura("./src/worms/images/" + prefix + "_iddle_baixo.png"), new Dimension(103, 104), 20);
		Animacao animacaoIddleGigante = new Animacao(prefix + "_iddle_gigante", new Textura("./src/worms/images/" + prefix + "_iddle_gigante.png"), new Dimension(103, 104), 20);
		Animacao animacaoIddleMedio = new Animacao(prefix + "_iddle_medio", new Textura("./src/worms/images/" + prefix + "_iddle_medio.png"), new Dimension(103, 104), 20);
		Animacao animacaoIddleNormal = new Animacao(prefix + "_iddle_normal", new Textura("./src/worms/images/" + prefix + "_iddle_normal.png"), new Dimension(103, 104), 20);
		Animacao animacaoDeath = new Animacao(prefix + "_death", new Textura("./src/worms/images/" + prefix + "_death.png"), new Dimension(138, 98), 20, false);

		return new Animacao[]
				{
					animacaoAlto, animacaoBaixo, animacaoGigante, animacaoMedio, animacaoNormal, animacaoIddleAlto, animacaoIddleBaixo, animacaoIddleGigante, animacaoIddleMedio, animacaoIddleNormal, animacaoDeath
				};
	}

	/**
	 * Retorna o id desse player
	 * @owner Victor
	 */
	public int GetId()
	{
		return _id;
	}

	/**
	 * Testa se o jogador já morreu
	 * @owner Victor
	 */
	public boolean IsMorto()
	{
		return _isMorto;
	}

	/**
	 * Retorna o angulo da arma do jogador
	 * @owner Victor
	 */
	public double GetAngulo()
	{
		int angulo = ANGULOS[GetDirecaoAtual()];

		if (GetLado() == LADO_ESQUERDO)
		{
			return Math.toRadians(angulo);
		} else if (GetLado() == LADO_DIREITO)
		{
			return Math.toRadians(180 - angulo);
		}

		return 0;
	}

	/**
	 * Retorna o lado do jogador
	 * @owner Victor
	 */
	public int GetLado()
	{
		return _ladoAtual;
	}

	/**
	 * Seta o lado atual do jogador
	 * @owner Victor
	 */
	public void SetLado(int lado)
	{
		_ladoAtual = lado;
		SetHorizontalFlip(lado == Player.LADO_DIREITO);
	}

	/**
	 * Retorna a direção da arma do jogador
	 * @owner Victor
	 */
	public int GetDirecaoAtual()
	{
		return _direcaoAtual;
	}

	/**
	 * Seta a direção da arma do jogador
	 * @owner Victor
	 */
	public void SetDirecaoAtual(int direcao)
	{
		_direcaoAtual = direcao;
		this.AtualizarAnimacao();
	}

	/**
	 * Retorna o estado atual do jogador
	 * @owner Victor
	 */
	public int GetEstadoJogador()
	{
		return _estadoAtual;
	}

	/**
	 * Seta o estado do jogador
	 * @owner Victor
	 */
	public void SetEstadoJogador(int estado)
	{
		_estadoAtual = estado;
		this.AtualizarAnimacao();
	}

	/**
	 * Retorna a vida atual do jogador
	 * @owner Victor
	 */
	public int GetVida()
	{
		return _vidaAtual;
	}

	/**
	 * Aumenta ou diminui a vida do jogador dependendo do modificador passado
	 * @param modificacao
	 */
	public void DominuirVida(int modificacao)
	{
		assert (modificacao < 0) : "Modificação inválida na vida!";

		int novaVida = GetVida() + modificacao;
		if (novaVida < 0)
		{
			novaVida = 0;
		} else if (novaVida > VIDA_MAXIMA)
		{
			novaVida = VIDA_MAXIMA;
		}

		if (_vidaAtual > novaVida && novaVida == 0)
		{
			for (PlayerStateListener listener : listeners)
			{
				listener.OnMorreu(this);
			}

			Morrer();
		}

		_vidaAtual = novaVida;

		for (PlayerStateListener listener : listeners)
		{
			listener.OnVidaDiminuida(this);
		}
	}

	/**
	 * Retorna o prefixo identificador do jogador
	 * @owner Victor
	 */
	protected String GetPrefix()
	{
		return _prefix;
	}

	/**
	 * Move o jogador em x de acordo com o estado dele
	 * @owner Victor
	 */
	private void MoverJogador()
	{
		SetPosicao(GetPosicao().x + (VELOCIDADE * GetEstadoJogador()), GetPosicao().y);
	}

	protected Player(int id, Point posicao, String prefix, GameContainer container)
	{
		super(posicao, LoadAnimacoes(prefix));
		_prefix = prefix;
		_direcaoAtual = 1;
		_id = id;
		this.container = container;
	}

	/**
	 * Atualiza a animação do jogador, e toca outra caso a direção da arma ou o estado tenha alterado
	 * @owner Victor
	 */
	public void AtualizarAnimacao()
	{
		if (GetEstadoJogador() == ESTADO_PARADO)
		{
			SetAnimation(GetPrefix() + "_iddle_" + DIRECOES[GetDirecaoAtual()]);
		} else if (GetEstadoJogador() == ESTADO_ANDANDO_FRENTE || GetEstadoJogador() == ESTADO_ANDANDO_TRAS)
		{
			SetAnimation(GetPrefix() + "_" + DIRECOES[GetDirecaoAtual()] + "_walk");
		}
	}

	/**
	 * Faz o player morrer, tocando a animação e enviando mensagens
	 */
	public void Morrer()
	{
		SetAnimation(GetPrefix() + "_death");
		_isMorto = true;
	}

	/**
	 * Adiciona um listener de mudança de estado do player
	 * @owner Victor
	 */
	public void AddListener(PlayerStateListener listener)
	{
		listeners.add(listener);
	}

	/**
	 * Remove um listener de mudança de estado do player
	 * @owner Victor
	 */
	public void RemoveListener(PlayerStateListener listener)
	{
		listeners.remove(listener);
	}

	@Override
	/**
	 * Atualiza a movimentação e a animação do jogador
	 * @owner Victor
	 */
	public void Atualizar(float milisegundos)
	{
		super.Atualizar(milisegundos);
		int posicaoPlataforma = 0;
		Rectangle colisaoPlataforma = new Rectangle(GetPosicao().x + (-1)*_ladoAtual*GetRetanguloDesenho().width / 2, GetPosicao().y + GetRetanguloDesenho().height, 1, 1);

		if ((GetEstadoJogador() == ESTADO_ANDANDO_FRENTE || GetEstadoJogador() == ESTADO_ANDANDO_TRAS))
		{
			MoverJogador();
		}

		boolean colidindoComPlataforma = false;

		for (Object objetoSprite : container.GetAllSprites())
		{
			if (objetoSprite instanceof Plataforma)
			{
				Plataforma plataforma = (Plataforma) objetoSprite;
				if (colisaoPlataforma.intersects(plataforma.GetRetanguloPlataformaSuperior()))
				{

					posicaoPlataforma = plataforma.GetPosicao().y;
					colidindoComPlataforma = true;
					break;
				}
			}
		}

		if (!colidindoComPlataforma && !IsMorto())
		{
			velocidadeY += GRAVIDADE;
			SetPosicao(GetPosicao().x, (int) (GetPosicao().y + velocidadeY));
		} else if (colidindoComPlataforma)
		{
			velocidadeY = 0;
			SetPosicao(GetPosicao().x, posicaoPlataforma - GetRetanguloDesenho().height);
			isPulando = false;
		}
	}

	/**
	 * Cria um inimigo com todas as animações
	 * @owner Victor
	 */
	public static Player CreateInimigo(int id, Point posicao, GameContainer container)
	{
		Player newPlayer = new Player(id, posicao, PREFIXO_INIMIGO, container);
		newPlayer.SetEstadoJogador(ESTADO_PARADO);

		return newPlayer;
	}

	public void Pular()
	{
		if (!isPulando && velocidadeY >= 0)
		{
			SetPosicao(GetPosicao().x, GetPosicao().y - 2);
			velocidadeY = -15.0f;
			isPulando = true;
		}
	}
}
